package licode.unisop.auth.application.uniclient;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import licode.unisop.auth.exception.AuthErrorCode;
import licode.unisop.client.api.auth.SopResourceQuery;
import licode.unisop.client.api.auth.SopUserAuthQuery;
import licode.unisop.client.info.*;
import licode.unisop.client.vo.*;
import licode.unisop.platform.client.api.SiPlatformAuthService;
import licode.unisop.platform.client.api.SiPlatformQueryService;
import licode.unisop.provider.conf.SiAssetType;
import licode.unisop.provider.info.SiHoldInfo;
import licode.unisop.provider.info.SiLookupAssetIn;
import licode.unisop.provider.vo.SiAsset;
import licode.unisop.provider.vo.SiAssetBO;
import licode.unisop.provider.vo.SiAssetHold;
import licode.unisop.provider.vo.SiProps;
import licode.unisop.types.AuthConst;
import org.bouncycastle.util.Strings;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 实现资源接口
 *
 * @author licode
 */
@Primary
@Component
public class SopResourceQueryImpl implements SopResourceQuery {
    @Resource
    private SopUserAuthQuery sopUserAuthQuery;

    @Resource
    private SiPlatformAuthService siPlatformAuthService;

    @Resource
    private SiPlatformQueryService siPlatformQueryService;

    @Override
    public SopUserType getUserType(SopPlatUserFind userFind) {
        return null;
    }

    @Override
    public SopUserAssets getUserAssets(SopTokenInfo tokenInfo) {
        SopUserAssets userAssets;
        SiAssetHold assetHold = findUserAsset(tokenInfo);

        if (null != assetHold) {
            userAssets = new SopUserAssets();
            userAssets.setUserId(assetHold.getHolderId());

            if (assetHold.hasIdAssets()) {
                userAssets.setRoles(new HashMap<>());
                userAssets.setPermits(new HashMap<>());

                assetHold.getIds().forEach((key, val) -> {
                    if (SiAssetType.AT_ROLE.equals(val.getType())) {
                        userAssets.getRoles().put(key, BeanUtil.copyProperties(val, SopRole.class));
                    } else if (SiAssetType.AT_PERMIT.equals(val.getType())) {
                        userAssets.getPermits().put(key, BeanUtil.copyProperties(val, SopPermit.class));
                    }
                });
            }

            if (assetHold.hasCodeTrees()) {
                Map<String, String> roles = assetHold.getCodes().get(SiAssetType.AT_ROLE);
                if (null != roles) {
                    userAssets.setRoleCodes(new HashMap<>());
                    roles.forEach((key, val) ->
                            userAssets.getRoleCodes().put(key, val));
                }
                Map<String, String> permits = assetHold.getCodes().get(SiAssetType.AT_PERMIT);
                if (null != permits) {
                    userAssets.setPermitCodes(new HashMap<>());
                    permits.forEach((key, val) ->
                            userAssets.getPermitCodes().put(key, val));
                }
            }

            if (assetHold.hasTrees()) {
                List<SiAssetBO> menus = assetHold.getTrees().get(SiAssetType.AT_MENU);
                if (null != menus) {
                    userAssets.setMenus(new ArrayList<>());
                    menus.forEach(item -> userAssets.getMenus().add(convertSopMenu(item)));
                }
            }
        } else {
            userAssets = null;
        }

        return userAssets;
    }

    @Override
    public SopUrlMatch matchUrl(SopUrlInfo urlInfo) {
        return SopUrlMatch.ok(true);
    }

    @Override
    public SopUrlMatch ssoMatchUrl(SopUrlInfo urlInfo) {
        List<SiAsset> assets;
        assets = siPlatformAuthService.lookPlatformAsset(SiLookupAssetIn.builder()
                .platformId(urlInfo.getInfo().getPlatformId())
                .assetType(SiAssetType.AT_URI).build());
        return SopUrlMatch.ok(true);
    }

    @Override
    public SopVerifyCode hasRole(SopRoleInfo roleInfo) {
        Map<String, String> roles = null;
        SiAssetHold assetHold;
        SiRoleAction action = roleInfo.getAction();

        if (StrUtil.isBlank(action.getRole())) {
            return SopVerifyCode.NO_CODE;
        }

        assetHold = findUserAsset(roleInfo.getInfo());

        if (null != assetHold) {
            if (assetHold.hasCodeTrees()) {
                roles = assetHold.getCodes().get(SiAssetType.AT_ROLE);
            }
            return handleCode(roles, action.getRole(), action.isAll());
        } else {
            return SopVerifyCode.NO_LOGIN;
        }
    }

    @Override
    public SopVerifyCode hasPermit(SopPermitInfo permitInfo) {
        Map<String, String> permits;
        SiAssetHold assetHold;
        SiPermitAction action = permitInfo.getAction();

        if (StrUtil.isBlank(action.getPermit())) {
            return SopVerifyCode.NO_CODE;
        }

        assetHold = findUserAsset(permitInfo.getInfo());

        if (null != assetHold && assetHold.hasCodeTrees()) {
            permits = assetHold.getCodes().get(SiAssetType.AT_PERMIT);
            return handleCode(permits, action.getPermit(), action.isAll());
        } else {
            return SopVerifyCode.NO_LOGIN;
        }
    }

    @Override
    public SopVerifyCode hasAuthority(SopAuthorityIn info) {
        Map<String, String> roles;
        Map<String, String> permits;
        SiAssetHold assetHold;

        SiAuthorityAction action = info.getAction();
        boolean hasRole = StrUtil.isNotBlank(action.getRoles());
        boolean hasPermit = StrUtil.isNotBlank(action.getPermits());

        if (!hasRole && !hasPermit) {
            return SopVerifyCode.NO_CODE;
        }

        assetHold = findUserAsset(info.getInfo());

        if (null != assetHold && assetHold.hasCodeTrees()) {
            if (hasRole) {
                roles = assetHold.getCodes().get(SiAssetType.AT_ROLE);
                SopVerifyCode role = handleCode(roles, action.getRoles(), action.isAllRole());
                if (role == SopVerifyCode.OK && !action.isAll()) {
                    return role;
                }
            }

            if (hasPermit) {
                permits = assetHold.getCodes().get(SiAssetType.AT_PERMIT);
                return handleCode(permits, action.getPermits(), action.isAllPermit());
            }
            return SopVerifyCode.FAIL;
        } else {
            return SopVerifyCode.NO_LOGIN;
        }
    }

    private SopVerifyCode handleCode(Map<String, String> codeMap, String code, boolean all) {
        if (null != codeMap) {
            boolean hasEqual = false;
            String[] codes = Strings.split(code, ',');
            for (String item : codes) {
                boolean hasCode = codeMap.containsKey(item);
                if (hasCode) {
                    hasEqual = true;
                }
                if (hasCode && !all) {
                    break;
                } else if (!hasCode && all) {
                    return SopVerifyCode.FAIL;
                }
            }
            return hasEqual ? SopVerifyCode.OK : SopVerifyCode.FAIL;
        }
        return SopVerifyCode.FAIL;
    }

    private SopMenu convertSopMenu(SiAssetBO asset) {
        SopMenu menu;
        SiProps props = asset.getProps();
        menu = BeanUtil.copyProperties(asset, SopMenu.class, "children");

        if (null != props) {
            props = SiProps.build(props);
            menu.setRouteName(props.getString("routeName", ""));
            menu.setRoutePath(props.getString("routePath", ""));
            menu.setIcon(props.getString("icon", ""));
            menu.setComponent(props.getString("component", ""));
            menu.setKeepAlive(props.getInt("keepAlive", 1));
            menu.setMenuType(props.getInt("menuType", 1));

            props.removeProp("routeName");
            props.removeProp("routePath");
            props.removeProp("icon");
            props.removeProp("component");
            props.removeProp("keepAlive");
            props.removeProp("menuType");

            menu.setProps(props);
        }

        if (asset.hasChildren()) {
            menu.setChildren(new ArrayList<>());
            asset.getChildren().forEach(item -> {
                menu.getChildren().add(convertSopMenu(item));
            });
        }

        return menu;
    }

    public SiAssetHold findUserAsset(SopTokenInfo tokenInfo) {
        SiAssetHold assetHold;
        SopAuthBrief brief = sopUserAuthQuery.getAuthBrief(tokenInfo);

        if (null == brief) {
            throw AuthErrorCode.ACCESS_TOKEN_EXPIRED.buildThrow();
        }

        SiHoldInfo holdInfo = SiHoldInfo.builder()
                .holdId(brief.getUserId())
                .tenantId(brief.getTenantId())
                .clientId(brief.getClientId())
                .platformId(brief.getPlatformId())
                .build();

        assetHold = siPlatformQueryService.getUserAssets(holdInfo);

        if (null != assetHold && null != assetHold.getCodes()) {
            Map<String, String> roleCodes = assetHold.getCodes().get("ROLE");
            // 超级管理员，重新获取所有资源
            if (null != roleCodes && roleCodes.containsKey(AuthConst.SYSTEM_ADMIN)) {
                holdInfo.setHoldId("");
                assetHold = siPlatformQueryService.getUserAssets(holdInfo);
            }
        }

        if (null != assetHold) {
            assetHold.setHolderId(brief.getUserId());
        }

        return assetHold;
    }
}
